package com.kaka.jsonmap.core.mapper;

import com.kaka.jsonmap.core.ValueTransformer;
import com.sun.tools.javac.util.Pair;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * DefaultFieldMapper is the default implementation of the FieldMapper interface.
 * It maps fields from a source object to a target object based on mapping definitions,
 * supporting dynamic key parsing and value transformations.
 */
@Slf4j
public class DefaultFieldMapper implements FieldMapper {

    private final ValueTransformer valueTransformer;

    public DefaultFieldMapper(ValueTransformer valueTransformer) {
        if (valueTransformer == null) {
            throw new IllegalArgumentException("ValueTransformer cannot be null");
        }
        this.valueTransformer = valueTransformer;
    }

    @Override
    public Map<String, Object> mapFields(Map<String, Object> mappingDefinitions, Map<String, Object> sourceObject) {
        Map<String, Object> result = new HashMap<>();
        for (Entry<String, Object> entry : mappingDefinitions.entrySet()) {
            if (isSpecialFormat(entry.getKey())) {
                result.putAll(mapSpecialFormatField(entry.getKey(), entry.getValue(), sourceObject));
            } else {
                Object value = valueTransformer.transformValue(entry.getKey(), entry.getValue(), sourceObject);
                result.put(entry.getKey(), value);
            }
        }
        return result;
    }

    /**
     * Processes a single mapping entry, parsing the key, merging rules, and mapping the value.
     *
     * @param key          The mapping entry to process
     * @param value        The source object to map from
     * @param sourceObject The result map to store the mapped value
     */
    private Map<String, Object> mapSpecialFormatField(String key, Object value, Map<String, Object> sourceObject) {
        Map<String, Object> result = new HashMap<>();
        Pair<String, String> parsed = parseKey(key);
        if (parsed == null) {
            result.put(key, value);
            return result;
        }
        String fieldName = parsed.fst;
        String expression = parsed.snd;
        Object expressionResult = this.valueTransformer.transformValue(key, expression, sourceObject);
        if (expressionResult == null) {
            result.put(fieldName, value instanceof List ? new ArrayList<>() : null);
            return result;
        }
        if (value instanceof List) {
            if (!(expressionResult instanceof List)) {
                result.put(fieldName, new ArrayList<>());
                return result;
            }
            List<?> subfieldMappingsList = (List<?>) value;
            if (subfieldMappingsList.isEmpty()) {
                result.put(fieldName, new ArrayList<>());
                return result;
            }
            // if the expression result is a list, we need to map each element to the subfield mappings
            List<Object> collection = (List<Object>) expressionResult;
            List<Map<String, Object>> mappedCollection = new ArrayList<>();
            Map<String, Object> subfieldMappings = (Map<String, Object>) subfieldMappingsList.get(0);
            for (Object element : collection) {
                if (!(element instanceof Map)) {
                    continue;
                }
                mappedCollection.add(mapSubFields(subfieldMappings, (Map<String, Object>) element, sourceObject));
            }
            result.put(fieldName, mappedCollection);
        } else if (value instanceof Map) {
            if (!(expressionResult instanceof Map)) {
                result.put(fieldName, null);
                return result;
            }
            Map<String, Object> subfieldMappings = (Map<String, Object>) value;
            result.put(fieldName, mapSubFields(subfieldMappings, (Map<String, Object>) expressionResult, sourceObject));
        } else {
            result.put(fieldName, expressionResult);
        }
        return result;

    }

    private Map<String, Object> mapSubFields(Map<String, Object> subfieldMappings, Map<String, Object> currentElement, Map<String, Object> sourceObject) {
        Map<String, Object> mappedElement = new HashMap<>();
        for (Map.Entry<String, Object> entry : subfieldMappings.entrySet()) {
            if (isCurrentOrRootSource(entry.getValue())) {
                mappedElement.put(entry.getKey(), valueTransformer.transformValue(entry.getKey(), entry.getValue(), currentElement));
            } else {
                mappedElement.put(entry.getKey(), valueTransformer.transformValue(entry.getKey(), entry.getValue(), sourceObject));
            }
        }
        return mappedElement;
    }

    private boolean isCurrentOrRootSource(Object value) {
        return valueTransformer.isCurrJsonPathEvaluator(value);
    }


    private boolean isSpecialFormat(String key) {
        return key.startsWith("{") && key.endsWith("}");
    }

    private Pair<String, String> parseKey(String key) {
        if (!isSpecialFormat(key)) {
            return null;
        }
        String expressionString = key.substring(1, key.length() - 1);
        String[] parts = expressionString.split(":");
        if (parts.length > 2) {
            throw new IllegalArgumentException("Invalid key format: " + key);
        }
        String fieldName = parts[0].trim();
        String rule = parts.length > 1 ? parts[1].trim() : null;
        return Pair.of(fieldName, rule);
    }

}